package com.zg.datapush.engine;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zg.common.core.dao.system.SysDatasource;
import com.zg.common.core.dao.system.SysDatasourceDao;
import com.zg.common.dds.DataSourceUtil;
import com.zg.datapush.constant.DataPushConstants;
import com.zg.common.core.dao.datapush.dao.DpFieldMappingDao;
import com.zg.common.core.dao.datapush.entity.DpFieldMapping;
import com.zg.common.core.dao.datapush.entity.DpPushTask;
import com.zg.datapush.exception.DataPushException;
import com.zg.datapush.handler.PushResult;
import com.zg.datapush.service.PushLogRecorder;
import com.zg.datapush.util.DataPushUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 数据库推送引擎
 * @author zg
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class DatabasePushEngine {

    private final SysDatasourceDao sysDatasourceDao;
    private final DpFieldMappingDao fieldMappingDao;
    private final DataTransformEngine dataTransformEngine;
    private final PushLogRecorder pushLogRecorder;
    private final DataSourceUtil dataSourceUtil;
    private final JdbcTemplate jdbcTemplate;

    /**
     * 执行数据库推送（单条推送模式）
     */
    public PushResult executeDatabasePush(DpPushTask task) {
        try {
            // 1. 获取源数据
            List<Map<String, Object>> sourceData = pushLogRecorder.executeWithLog(
                "获取源数据",
                this.getClass().getName(),
                "getSourceData",
                task,
                () -> getSourceData(task)
            );

            if (sourceData.isEmpty()) {
                PushResult result = PushResult.success("没有需要推送的数据", 0);
                result.setSuccessCount(0);
                result.setFailedCount(0);
                return result;
            }

            // 2. 获取字段映射配置
            List<DpFieldMapping> fieldMappings = pushLogRecorder.executeWithLog(
                "获取字段映射配置",
                this.getClass().getName(),
                "getFieldMappings",
                task.getId(),
                () -> getFieldMappings(task.getId())
            );

            // 3. 验证目标数据源存在
            pushLogRecorder.executeWithLog(
                "验证目标数据源",
                this.getClass().getName(),
                "validateTargetDatasource",
                task.getTargetDatasourceCode(),
                () -> {
                    SysDatasource targetDs = getTargetDatasource(task.getTargetDatasourceCode());
                    return targetDs != null;
                }
            );

            // 4. 单条推送模式：逐条处理数据
            int successCount = 0;
            int failedCount = 0;
            StringBuilder errorMessages = new StringBuilder();

            // 使用DataSourceUtil切换到目标数据源
            pushLogRecorder.executeWithLog(
                "切换到目标数据源",
                this.getClass().getName(),
                "switchToTargetDataSource",
                task.getTargetDatasourceCode(),
                () -> {
                    dataSourceUtil.change(task.getTargetDatasourceCode());
                }
            );

            try {
                for (int i = 0; i < sourceData.size(); i++) {
                    Map<String, Object> singleRecord = sourceData.get(i);

                    try {
                        // 转换单条数据
                        Map<String, Object> transformedRecord = pushLogRecorder.executeWithLog(
                            String.format("数据转换-第%d条", i + 1),
                            DataTransformEngine.class.getName(),
                            "transformSingleRecord",
                            singleRecord,
                            () -> dataTransformEngine.transformSingleRecord(singleRecord, fieldMappings)
                        );

                        // 推送单条数据到数据库
                        pushLogRecorder.executeWithLog(
                            String.format("推送数据-第%d条", i + 1),
                            this.getClass().getName(),
                            "pushSingleRecord",
                            transformedRecord,
                            () -> {
                                pushSingleRecordToDatabase(task, transformedRecord, jdbcTemplate);
                            }
                        );

                        successCount++;

                    } catch (Exception e) {
                        failedCount++;
                        String errorMsg = String.format("第%d条数据推送失败: %s", i + 1, e.getMessage());
                        errorMessages.append(errorMsg).append("; ");
                        log.error(errorMsg, e);
                    }
                }
            } finally {
                // 清理数据源切换
                dataSourceUtil.clear();
            }

            // 5. 构建推送结果
            PushResult result;
            if (failedCount == 0) {
                result = PushResult.success(String.format("推送完成，成功%d条", successCount));
            } else if (successCount == 0) {
                result = PushResult.failure(String.format("推送失败，失败%d条: %s", failedCount, errorMessages.toString()));
            } else {
                result = PushResult.success(String.format("推送完成，成功%d条，失败%d条: %s", successCount, failedCount, errorMessages.toString()));
            }

            result.setSuccessCount(successCount);
            result.setFailedCount(failedCount);
            return result;

        } catch (Exception e) {
            log.error("数据库推送失败", e);
            throw new DataPushException(DataPushException.ErrorCodes.SQL_EXECUTION_ERROR,
                                      "数据库推送失败: " + e.getMessage(), e);
        }
    }

    /**
     * 获取源数据
     */
    private List<Map<String, Object>> getSourceData(DpPushTask task) {
        // 验证表名安全性
        if (!DataPushUtil.isValidTableName(task.getSourceTableName())) {
            throw new DataPushException(DataPushException.ErrorCodes.VALIDATION_ERROR,
                                      "无效的源表名: " + task.getSourceTableName());
        }

        // 验证源数据源是否存在
        SysDatasource sourceDs = sysDatasourceDao.getOne(
            new LambdaQueryWrapper<SysDatasource>()
                .eq(SysDatasource::getCode, task.getSourceDatasourceCode())
        );

        if (sourceDs == null) {
            throw new DataPushException(DataPushException.ErrorCodes.DATASOURCE_NOT_FOUND,
                                      "源数据源不存在: " + task.getSourceDatasourceCode());
        }

        // 使用DataSourceUtil切换到源数据源
        dataSourceUtil.change(task.getSourceDatasourceCode());

        try {
            String sql = buildSourceSql(task);
            return jdbcTemplate.queryForList(sql);
        } finally {
            // 清理数据源切换
            dataSourceUtil.clear();
        }
    }

    /**
     * 获取字段映射配置
     */
    private List<DpFieldMapping> getFieldMappings(Long taskId) {
        return fieldMappingDao.list(
            new LambdaQueryWrapper<DpFieldMapping>()
                .eq(DpFieldMapping::getTaskId, taskId)
                .orderByAsc(DpFieldMapping::getSortOrder)
        );
    }

    /**
     * 获取目标数据源
     */
    private SysDatasource getTargetDatasource(String datasourceCode) {
        SysDatasource targetDs = sysDatasourceDao.getOne(
            new LambdaQueryWrapper<SysDatasource>()
                .eq(SysDatasource::getCode, datasourceCode)
        );

        if (targetDs == null) {
            throw new DataPushException(DataPushException.ErrorCodes.DATASOURCE_NOT_FOUND,
                                      "目标数据源不存在: " + datasourceCode);
        }
        return targetDs;
    }

    /**
     * 推送单条记录到数据库
     */
    private void pushSingleRecordToDatabase(DpPushTask task, Map<String, Object> data, JdbcTemplate targetJdbcTemplate) {
        // 验证目标表名安全性
        if (!DataPushUtil.isValidTableName(task.getTargetTableName())) {
            throw new DataPushException(DataPushException.ErrorCodes.VALIDATION_ERROR,
                                      "无效的目标表名: " + task.getTargetTableName());
        }

        if (data.isEmpty()) {
            return;
        }

        // 构建插入SQL
        String insertSql = buildInsertSql(task.getTargetTableName(), data);
        Object[] args = data.values().toArray();

        // 执行插入
        int updateCount = targetJdbcTemplate.update(insertSql, args);
        if (updateCount <= 0) {
            throw new RuntimeException("数据插入失败，影响行数: " + updateCount);
        }
    }



    /**
     * 构建源数据查询SQL
     */
    private String buildSourceSql(DpPushTask task) {
        StringBuilder sql = new StringBuilder("SELECT *")
                                                      .append(" FROM ").append(task.getSourceTableName());

        // 增量推送条件
        if (DataPushConstants.PUSH_MODE_INCREMENT.equals(task.getPushMode()) &&
            task.getIncrementField() != null &&
            task.getLastPushTime() != null) {

            // 使用Oracle的日期格式
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String formattedDate = dateFormat.format(task.getLastPushTime());

            sql.append(" WHERE ").append(task.getIncrementField())
               .append(" > TO_DATE('").append(formattedDate).append("', 'YYYY-MM-DD HH24:MI:SS')");
        }

        return sql.toString();
    }

    /**
     * 构建插入SQL
     */
    private String buildInsertSql(String tableName, Map<String, Object> record) {
        String columns = String.join(",", record.keySet());
        String placeholders = record.keySet().stream()
            .map(k -> "?")
            .collect(Collectors.joining(","));
        return String.format("INSERT INTO %s (%s) VALUES (%s)", tableName, columns, placeholders);
    }
}
